package io.gatling.jsonbenchmark.serializerTests; import groovy.json.DateFormatThreadLocal; import groovy.json.JsonDelegate; import groovy.json.JsonException; import groovy.json.JsonLexer; import groovy.json.JsonOutput; import groovy.json.JsonToken; import groovy.json.JsonTokenType; import groovy.json.StringEscapeUtils; import groovy.lang.Closure; import groovy.lang.GString; import groovy.util.Expando; import java.io.StringReader; import java.lang.*;import java.lang.Boolean;import java.lang.Character;import java.lang.Double;import java.lang.Enum;import java.lang.Float;import java.lang.IllegalArgumentException;import java.lang.Number;import java.lang.Object;import java.lang.Override;import java.lang.String;import java.lang.StringBuilder;import java.lang.ThreadLocal;import java.net.URL; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; import java.util.Queue; import java.util.UUID; import org.codehaus.groovy.runtime.DefaultGroovyMethods; /** * Rewritten {@link JsonOutput} to Java. * * @author Andrey Bloschetsov */ public class MyJsonOutput { private static final ThreadLocal<SimpleDateFormat> dateFormatter = new DateFormatThreadLocal(); /** * Simply rewritten to Java with minor changes. */ public static String toJson(Object object) { if (object == null) { return JsonBlock.NULL.symbol; } else if (object instanceof GString) { return new StringBuilder().append(JsonBlock.DQUOTE.symbol).append(StringEscapeUtils.escapeJava(((GString) object).toString())) .append(JsonBlock.DQUOTE.symbol).toString(); } else if (object instanceof String) { return new StringBuilder().append(JsonBlock.DQUOTE.symbol).append(StringEscapeUtils.escapeJava((String) object)).append(JsonBlock.DQUOTE.symbol) .toString(); } else if (object instanceof java.lang.Boolean) { return object.toString(); } else if (object instanceof Number) { if ((object.getClass() == Double.class && (((Double) object).isInfinite() || ((Double) object).isNaN())) || (object.getClass() == Float.class && (((Float) object).isInfinite() || ((Float) object).isNaN()))) { throw new JsonException("Number " + object + " can't be serialized as JSON: infinite or NaN are not allowed in JSON."); } return object.toString(); } else if (object instanceof Date) { return new StringBuilder().append(JsonBlock.DQUOTE.symbol).append(dateFormatter.get().format(object)).append(JsonBlock.DQUOTE.symbol).toString(); } else if (object instanceof Calendar) { return new StringBuilder().append(JsonBlock.DQUOTE.symbol).append(dateFormatter.get().format(((Calendar) object).getTime())) .append(JsonBlock.DQUOTE.symbol).toString(); } else if (object instanceof Collection) { Collection<?> collection = (Collection<?>) object; StringBuilder sb = new StringBuilder(JsonBlock.START_LIST.symbol); Iterator<?> iterator = collection.iterator(); if (iterator.hasNext()) { Object it = iterator.next(); sb.append(toJson(it)); while (iterator.hasNext()) { it = iterator.next(); sb.append(JsonBlock.COMMA.symbol); sb.append(toJson(it)); } } sb.append(JsonBlock.END_LIST.symbol); return sb.toString(); } else if (object instanceof Iterator) { Iterator<?> iterator = (Iterator<?>) object; StringBuilder sb = new StringBuilder(JsonBlock.START_LIST.symbol); if (iterator.hasNext()) { Object it = iterator.next(); sb.append(toJson(it)); while (iterator.hasNext()) { it = iterator.next(); sb.append(JsonBlock.COMMA.symbol); sb.append(toJson(it)); } } sb.append(JsonBlock.END_LIST.symbol); return sb.toString(); } else if (object instanceof Map) { StringBuilder sb = new StringBuilder(JsonBlock.START_OBJECT.symbol); Map<?, ?> m = (Map<?, ?>) object; if (!m.isEmpty()) { boolean firstItem = true; for (Entry<?, ?> entry : m.entrySet()) { if (entry.getKey() == null) { throw new IllegalArgumentException("Maps with null keys can\'t be converted to JSON"); } if (!firstItem) { sb.append(JsonBlock.COMMA.symbol); } else { firstItem = false; } sb.append(JsonBlock.DQUOTE.symbol).append(entry.getKey().toString()).append(JsonBlock.DQUOTE.symbol).append(JsonBlock.COLON.symbol) .append(toJson(entry.getValue())); } } else { sb.append(JsonBlock.COLON.symbol); } return sb.append(JsonBlock.END_OBJECT.symbol).toString(); } else if (object instanceof URL || object instanceof UUID || object instanceof Character) { return new StringBuilder().append(JsonBlock.DQUOTE.symbol).append(object.toString()).append(JsonBlock.DQUOTE.symbol).toString(); } else if (object instanceof Closure) { return toJson(JsonDelegate.cloneDelegateAndGetContent((Closure<?>) object)); } else if (object instanceof Expando) { return toJson(((Expando) object).getProperties()); } else if (object instanceof Enumeration) { Enumeration<?> enumeration = (Enumeration<?>) object; StringBuilder sb = new StringBuilder().append(JsonBlock.START_LIST.symbol); if (enumeration.hasMoreElements()) { Object it = enumeration.nextElement(); sb.append(toJson(it)); while (enumeration.hasMoreElements()) { it = enumeration.nextElement(); sb.append(JsonBlock.COMMA.symbol); sb.append(toJson(it)); } } sb.append(JsonBlock.END_LIST.symbol); return sb.toString(); } else if (object.getClass().isArray()) { StringBuilder sb = new StringBuilder().append(JsonBlock.START_LIST.symbol); boolean firstItem = true; if (object instanceof Object[]) { Object[] arr = (Object[]) object; for (Object it : arr) { if (!firstItem) { sb.append(JsonBlock.COMMA.symbol); } else { firstItem = false; } sb.append(toJson(it)); } } else if (object instanceof byte[]) { byte[] arr = (byte[]) object; for (byte it : arr) { if (!firstItem) { sb.append(JsonBlock.COMMA.symbol); } else { firstItem = false; } sb.append(it); } } else if (object instanceof char[]) { char[] arr = (char[]) object; for (char it : arr) { if (!firstItem) { sb.append(JsonBlock.COMMA.symbol); } else { firstItem = false; } sb.append(JsonBlock.DQUOTE.symbol).append(it).append(JsonBlock.DQUOTE.symbol); } } else if (object instanceof boolean[]) { boolean[] arr = (boolean[]) object; for (boolean it : arr) { if (!firstItem) { sb.append(JsonBlock.COMMA.symbol); } else { firstItem = false; } sb.append(Boolean.toString(it)); } } else if (object instanceof int[]) { int[] arr = (int[]) object; for (int it : arr) { if (!firstItem) { sb.append(JsonBlock.COMMA.symbol); } else { firstItem = false; } sb.append(it); } } else if (object instanceof long[]) { long[] arr = (long[]) object; for (long it : arr) { if (!firstItem) { sb.append(JsonBlock.COMMA.symbol); } else { firstItem = false; } sb.append(it); } } else if (object instanceof double[]) { double[] arr = (double[]) object; for (double it : arr) { if (!firstItem) { sb.append(JsonBlock.COMMA.symbol); } else { firstItem = false; } sb.append(it); } } else if (object instanceof float[]) { float[] arr = (float[]) object; for (float it : arr) { if (!firstItem) { sb.append(JsonBlock.COMMA.symbol); } else { firstItem = false; } sb.append(it); } } sb.append(JsonBlock.END_LIST.symbol); return sb.toString(); } else if (object instanceof Enum) { return new StringBuilder().append(JsonBlock.DQUOTE.symbol).append(((Enum<?>) object).name()).append(JsonBlock.DQUOTE.symbol).toString(); } else { Map<?, ?> properties = DefaultGroovyMethods.getProperties(object); properties.remove("class"); properties.remove("declaringClass"); properties.remove("metaClass"); return toJson(properties); } } private static enum JsonBlock { START_LIST("["), END_LIST("]"), START_OBJECT("{"), END_OBJECT("}"), COMMA(","), COLON(":"), DQUOTE("\""), NULL("null"); String symbol; JsonBlock(String symbol) { this.symbol = symbol; } @Override public String toString() { return symbol; } } /** * Rewritten to Java without recursions. */ public static String toJsonWithoutRecursion(Object obj) { StringBuilder sb = new StringBuilder(); Queue<Object> stacks = Collections.asLifoQueue(new LinkedList<>()); stacks.add(obj); Queue<Object> temp = Collections.asLifoQueue(new LinkedList<>()); Object item; while (!stacks.isEmpty()) { item = stacks.poll(); if (item == null) { sb.append(JsonBlock.NULL.symbol); } else if (item instanceof JsonBlock) { sb.append(((JsonBlock) item).symbol); } else if (item instanceof GString) { sb.append(JsonBlock.DQUOTE.symbol).append(StringEscapeUtils.escapeJava(((GString) item).toString())).append(JsonBlock.DQUOTE.symbol); } else if (item instanceof String) { sb.append(JsonBlock.DQUOTE.symbol).append(StringEscapeUtils.escapeJava((String) item)).append(JsonBlock.DQUOTE.symbol); } else if (item instanceof Number) { if ((item.getClass() == Double.class && (((Double) item).isInfinite() || ((Double) item).isNaN())) || (item.getClass() == Float.class && (((Float) item).isInfinite() || ((Float) item).isNaN()))) { throw new JsonException("Number " + item + " can't be serialized as JSON: infinite or NaN are not allowed in JSON."); } sb.append(item.toString()); } else if (item instanceof Boolean) { sb.append(item.toString()); } else if (item instanceof Date) { sb.append(JsonBlock.DQUOTE.symbol).append(dateFormatter.get().format((Date) item)).append(JsonBlock.DQUOTE.symbol).toString(); } else if (item instanceof Calendar) { sb.append(JsonBlock.DQUOTE.symbol).append(dateFormatter.get().format(((Calendar) item).getTime())).append(JsonBlock.DQUOTE.symbol).toString(); } else if (item instanceof Collection || item instanceof Iterator || item instanceof Enumeration) { sb.append(JsonBlock.START_LIST.symbol); stacks.add(JsonBlock.END_LIST); Iterator<?> iterator; if (item instanceof Collection) { iterator = ((Collection<?>) item).iterator(); } else if (item instanceof Iterator) { iterator = (Iterator<?>) item; } else { iterator = Collections.list((Enumeration<?>) item).iterator(); } while (iterator.hasNext()) { temp.add(iterator.next()); } boolean first = true; while (!temp.isEmpty()) { if (!first) { stacks.add(JsonBlock.COMMA); } else { first = false; } stacks.add(temp.poll()); } } else if (item instanceof Map || item instanceof Expando) { sb.append(JsonBlock.START_OBJECT.symbol); stacks.add(JsonBlock.END_OBJECT); Map<?, ?> map; if (item instanceof Map) { map = (Map<?, ?>) item; } else { map = ((Expando) item).getProperties(); } if (!map.isEmpty()) { for (Entry<?, ?> entry : map.entrySet()) { if (entry.getKey() == null) { throw new IllegalArgumentException("Maps with null keys can\'t be converted to JSON"); } temp.add(entry.getKey().toString()); temp.add(entry.getValue()); } boolean firstPair = true; while (!temp.isEmpty()) { if (!firstPair) { stacks.add(JsonBlock.COMMA); } else { firstPair = false; } stacks.add(temp.poll()); // First add value stacks.add(JsonBlock.COLON); stacks.add(temp.poll()); // Then add key } } else { sb.append(JsonBlock.COLON.symbol); } } else if (item instanceof Enum) { sb.append(JsonBlock.DQUOTE.symbol).append(((Enum<?>) item).name()).append(JsonBlock.DQUOTE.symbol).toString(); } else if (item instanceof URL || item instanceof UUID || item instanceof Character) { sb.append(JsonBlock.DQUOTE.symbol).append(item.toString()).append(JsonBlock.DQUOTE.symbol).toString(); } else if (item instanceof Closure) { stacks.add(JsonDelegate.cloneDelegateAndGetContent((Closure<?>) item)); } else if (item.getClass().isArray()) { sb.append(JsonBlock.START_LIST.symbol); stacks.add(JsonBlock.END_LIST); if (item instanceof Object[]) { for (int i = ((Object[]) item).length - 1; i >= 0; i--) { stacks.add(((Object[]) item)[i]); if (i != 0) { stacks.add(JsonBlock.COMMA); } } } else if (item instanceof byte[]) { for (int i = ((byte[]) item).length - 1; i >= 0; i--) { sb.append(((byte[]) item)[i]); if (i != 0) { sb.append(JsonBlock.COMMA.symbol); } } } else if (item instanceof char[]) { for (int i = ((char[]) item).length - 1; i >= 0; i--) { sb.append(((char[]) item)[i]); if (i != 0) { sb.append(JsonBlock.COMMA.symbol); } } } else if (item instanceof int[]) { for (int i = ((int[]) item).length - 1; i >= 0; i--) { sb.append(((int[]) item)[i]); if (i != 0) { sb.append(JsonBlock.COMMA.symbol); } } } else if (item instanceof boolean[]) { for (int i = ((boolean[]) item).length - 1; i >= 0; i--) { sb.append(((boolean[]) item)[i]); if (i != 0) { sb.append(JsonBlock.COMMA.symbol); } } } else if (item instanceof long[]) { for (int i = ((long[]) item).length - 1; i >= 0; i--) { sb.append(((long[]) item)[i]); if (i != 0) { sb.append(JsonBlock.COMMA.symbol); } } } else if (item instanceof double[]) { for (int i = ((double[]) item).length - 1; i >= 0; i--) { sb.append(((double[]) item)[i]); if (i != 0) { sb.append(JsonBlock.COMMA.symbol); } } } else if (item instanceof float[]) { for (int i = ((float[]) item).length - 1; i >= 0; i--) { sb.append(((float[]) item)[i]); if (i != 0) { sb.append(JsonBlock.COMMA.symbol); } } } } else { Map<?, ?> properties = DefaultGroovyMethods.getProperties(item); properties.remove("class"); properties.remove("declaringClass"); properties.remove("metaClass"); stacks.add(properties); } } return sb.toString(); } public static String prettyPrint(String jsonPayload) { int indent = 0; StringBuilder output = new StringBuilder(); JsonLexer lexer = new JsonLexer(new StringReader(jsonPayload)); while (lexer.hasNext()) { JsonToken token = lexer.next(); if (token.getType() == JsonTokenType.OPEN_CURLY) { indent += 4; output.append("{\n").append(nSpance(indent)); } else if (token.getType() == JsonTokenType.CLOSE_CURLY) { indent -= 4; output.append("\n").append(nSpance(indent)).append("}"); } else if (token.getType() == JsonTokenType.OPEN_BRACKET) { indent += 4; output.append("[\n").append(nSpance(indent)); } else if (token.getType() == JsonTokenType.CLOSE_BRACKET) { indent -= 4; output.append("\n").append(nSpance(indent)).append("]"); } else if (token.getType() == JsonTokenType.COMMA) { output.append(",\n").append(nSpance(indent)); } else if (token.getType() == JsonTokenType.COLON) { output.append(": "); } else if (token.getType() == JsonTokenType.STRING) { String textStr = token.getText(); String textWithoutQuotes = textStr.substring(1, textStr.length() - 1); output.append("\"" + StringEscapeUtils.escapeJava(textWithoutQuotes) + "\""); } else { output.append(token.getText()); } } return output.toString(); } private static String nSpance(int n) { char[] spaces = new char[n]; Arrays.fill(spaces, ' '); return new String(spaces); } private MyJsonOutput() { } }